package com.meta.context;


import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.meta.annotation.MetaGenAnnotation;
import com.meta.annotation.MetaMapper;
import com.meta.annotation.MetaMappers;
import com.meta.builder.IMetaTemplate;
import com.meta.builder.freemarker.FreemarkerProcessor;
import com.meta.builder.freemarker.field.ClassInfo;
import com.meta.builder.freemarker.tmp.ClassTypeTmpEnum;
import com.meta.builder.schema.MeteCodeWriter;
import com.meta.builder.schema.config.MetaJsonschema2pojoConfig;
import com.meta.context.source.IMetaSource;
import com.meta.context.source.IMetaSourceFactory;
import com.meta.context.spi.MetaProcessingEnvironment;
import com.meta.internal.Options;
import com.sun.codemodel.JCodeModel;
import org.jsonschema2pojo.GenerationConfig;
import org.jsonschema2pojo.SchemaMapper;

import javax.annotation.processing.Messager;
import javax.lang.model.element.Element;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import java.io.IOException;
import java.util.*;
import java.util.function.Function;


public class MetaAnnotationProcessorContext implements MetaProcessingEnvironment {
    private Elements elementUtils;
    private Types typeUtils;
    private Messager messager;
    private FreemarkerProcessor freemarkerProcessor;
    private Options options;

    public MetaAnnotationProcessorContext(Elements elementUtils, Types typeUtils, Messager messager, FreemarkerProcessor freemarkerProcessor, Options options) {
        this.elementUtils = elementUtils;
        this.typeUtils = typeUtils;
        this.messager = messager;
        this.freemarkerProcessor = freemarkerProcessor;
        this.options = options;
    }

    public void process(Element element) {
        MetaGenAnnotation[] metaAnnotations = element.getAnnotationsByType(MetaGenAnnotation.class);

        String metaKey = getMetaAntKey(metaAnnotations);
        IMetaSource metaSource = IMetaSourceFactory.buildSource(options);
        JSONObject obj = metaSource.getSources(options).get(metaKey);

        IMetaTemplate metaTemplate = options.getTemplates().get(ClassTypeTmpEnum.OBJECT.getBuilder().getName());

        try {
            metaTemplate.initTemplate(element, options);
            List<ClassInfo> classInfoList = metaTemplate.builder(obj, element.getSimpleName() + "DTO", metaMappers(element), metaAnnotations);
            for (ClassInfo classInfo : classInfoList) {
                metaTemplate.setClassInfo(classInfo);
                freemarkerProcessor.writeSource(metaTemplate, element);
            }
            testWrite();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void testWrite() {
        // JSON数据
        String json = "{\"name1\":\"John\", \"age\":30}";

        // JSON Schema定义
        String schemaStr = "{\"minProperties\":1,\"type\":\"object\",\"$comment\":\"this is a mark for our injected plugin schema\",\"properties\":{\"_meta\":{\"type\":\"object\",\"properties\":{\"error_response\":{\"oneOf\":[{\"type\":\"string\"},{\"type\":\"object\"}]},\"filter\":{\"type\":\"array\",\"description\":\"filter determines whether the plugin needs to be executed at runtime\"},\"priority\":{\"type\":\"integer\",\"description\":\"priority of plugins by customized order\"},\"disable\":{\"type\":\"boolean\"}}},\"abort\":{\"type\":\"object\",\"required\":[\"http_status\"],\"properties\":{\"percentage\":{\"type\":\"integer\",\"maximum\":100,\"minimum\":0},\"headers\":{\"type\":\"object\",\"patternProperties\":{\"^[^:]+$\":{\"oneOf\":[{\"type\":\"string\"},{\"type\":\"number\"}]}},\"minProperties\":1},\"vars\":{\"type\":\"array\",\"maxItems\":20,\"items\":{\"type\":\"array\"}},\"body\":{\"type\":\"string\",\"minLength\":0},\"http_status\":{\"type\":\"integer\",\"minimum\":200}}},\"delay\":{\"type\":\"object\",\"required\":[\"duration\"],\"properties\":{\"percentage\":{\"type\":\"integer\",\"maximum\":100,\"minimum\":0},\"duration\":{\"type\":\"number\",\"minimum\":0},\"vars\":{\"type\":\"array\",\"maxItems\":20,\"items\":{\"type\":\"array\"}}}}}}";
//        String schemaStr = "{ \"type\": \"object\",\n  \"properties\": {\n   \"name\": {\"type\": \"string\"},\n    \"age\": {\"type\": \"integer\"}\n" +
//                "   },\n" +
//                "   \"required\": [\"name\", \"age\"]\n" +
//                "}";

        JCodeModel codeModel = new JCodeModel();
        try {
            GenerationConfig config = MetaJsonschema2pojoConfig.getGenerationConfig();
            SchemaMapper mapper = MetaJsonschema2pojoConfig.getDefaultSchemaMapper();
            PropertyNamingStrategy namingStrategy = PropertyNamingStrategies.SNAKE_CASE;

//            config.setPropertyNamingStrategy(namingStrategy);

            // 设置属性名称转换函数
            Function<String, String> replacementFunction = (originalName) -> {
                if ("id".equalsIgnoreCase(originalName)) {
                    return "identifier";
                } else {
                    return originalName;
                }
            };
//            config.setPropertyNameReplacement(replacementFunction);

//            JavaFileObject fileObject = options.getProcessingEnv().getFiler().createSourceFile("vip.mate.gate.plugin.dto.ClassName1");
//            OutputStream outputStream = fileObject.openOutputStream();
            mapper.generate(codeModel, "ClassName1", "vip.mate.gate.plugin.dto", schemaStr);
            MeteCodeWriter writer = new MeteCodeWriter(options);

            codeModel.build(writer);

            codeModel.packages().forEachRemaining(jPackage -> {
                options.getProcessingEnv().getMessager().printMessage(Diagnostic.Kind.WARNING, "======doing pkgName: " + jPackage.name());
            });
//            codeModel.build(Files.createTempDirectory("required").toFile());
        } catch (IOException e) {
            options.getProcessingEnv().getMessager().printMessage(Diagnostic.Kind.WARNING, "======doing file plugin");
//            e.printStackTrace();
        }

    }

    /**
     * 获取注解源数据KEY值
     *
     * @param metaAnnotations meta注解
     * @return
     */
    private String getMetaAntKey(MetaGenAnnotation[] metaAnnotations) {
        String key = "";
        for (MetaGenAnnotation metaAnnotation : metaAnnotations) {
            key = metaAnnotation.name();
        }
        return key;
    }

    /**
     * 获取字段映射注解
     *
     * @param element 类节点
     * @return
     */
    private Map<String, MetaMapper> metaMappers(Element element) {
        List<MetaMapper> mappers = new ArrayList<>();
        Map<String, MetaMapper> mapperMap = new HashMap<>();
        MetaMappers[] metaMappers = element.getAnnotationsByType(MetaMappers.class);
        for (MetaMappers metaMapper : metaMappers) {
            mappers.addAll(Arrays.stream(metaMapper.value()).toList());
        }
        MetaMapper[] metaMapper = element.getAnnotationsByType(MetaMapper.class);
        for (MetaMapper mapper : metaMapper) {
            mappers.add(mapper);
        }
        options.getProcessingEnv().getMessager().printMessage(Diagnostic.Kind.NOTE, "======doing print ant" + metaMappers);
        for (MetaMapper mapper : mappers) {
            mapperMap.put(mapper.source(), mapper);
        }
        return mapperMap;
    }

    @Override
    public Elements getElementUtils() {
        return elementUtils;
    }

    @Override
    public Types getTypeUtils() {
        return typeUtils;
    }
}
